package main

import (
	"fmt"
	"strconv"
)

const fileFilter = "All File(*);;Bitmap Image File(*.bmp *.dib *.rle);;Icon Image File(*.ico);;Cursor Image File(*.cur)"

type Root = BitmapFileType

type BitmapFileType struct {
	Bitmap *BitmapFile
	Icon   *IconFile
}

func (v *BitmapFileType) Unmarshal(dec *Decoder, info *ValueInfo) {
	pos := dec.Pos()
	magic := dec.ReadBytes(2)
	dec.Seek(pos)

	if magic[0] == 0 && magic[1] == 0 {
		v.Icon = &IconFile{}
		dec.Decode(&v.Icon, info)
	} else {
		v.Bitmap = &BitmapFile{}
		dec.Decode(&v.Bitmap, info)
	}
}

/****************** 位图 **********************/

type BitmapFile struct {
	Header   BitmapFileHeader
	Info     BitmapInfoHeader
	Quad     BitmapQuad  `info:"调色板"`
	BitLines BitmapLines `info:"点阵数据"`
}

type BitmapFileHeader struct {
	Type      MagicBmp `info:"文件标识"`
	Size      uint32   `info:"文件大小"`
	Reserved1 uint16
	Reserved2 uint16
	OffBits   uint32 `info:"点阵数据偏移"`
}

type MagicBmp string

func (v *MagicBmp) Unmarshal(dec *Decoder, info *ValueInfo) {
	*v = MagicBmp(dec.ReadBytes(2))
	if *v != "BM" {
		panic("file isn't bitmap format")
	}
}

type BitmapInfoHeader BitmapInfo
type BitmapInfo struct {
	Size          uint32    `info:"信息头大小"`
	Width         int32     `info:"宽度(像素)"`
	Height        int32     `info:"高度(像素)"`
	Planes        uint16    `info:"颜色平面数"`
	BitCount      uint16    `info:"比特/像素"`
	Compression   uint32    `info:"压缩类型"`
	SizeImage     uint32    `info:"点阵数据大小"`
	XPelsPerMeter int32     `info:"水平分辨率(像素/米)"`
	YPelsPerMeter int32     `info:"垂直分辨率(像素/米)"`
	ClrUsed       uint32    `info:"调色板颜色数"`
	ClrImportant  uint32    `info:"关键颜色数"`
	ClrMask       ColorMask `info:"颜色掩码"`
}

func (v *BitmapInfoHeader) Unmarshal(dec *Decoder, info *ValueInfo) {
	dec.SetArg(v)
	dec.Decode((*BitmapInfo)(v), info)
}

type ColorMask []uint32

func (v *ColorMask) Unmarshal(dec *Decoder, info *ValueInfo) {
	bmp, ok := dec.Arg().(*BitmapInfoHeader)
	if !ok || info == nil {
		panic("decode arg not set")
	}

	if bmp.Size == 40 {
		return
	} else if bmp.Size != 52 {
		panic(fmt.Sprintf("unsupport BitmapInfoHeader size: %d", bmp.Size))
	}
	var tmp [3]uint32
	dec.Decode(&tmp, info)
	*v = tmp[:]
}

type BitmapQuad []RgbQuad
type RgbQuad struct {
	Blue     byte
	Green    byte
	Red      byte
	Reserved byte
}

func (v *BitmapQuad) Unmarshal(dec *Decoder, info *ValueInfo) {
	bmp, ok := dec.Arg().(*BitmapInfoHeader)
	if !ok || bmp == nil {
		panic("decode arg not set")
	}

	if bmp.BitCount >= 16 {
		return
	}
	num := bmp.ClrUsed
	if num == 0 {
		num = 1 << bmp.BitCount
	}
	tmp := make([]RgbQuad, num)
	dec.Decode(&tmp, info)
	*v = tmp
}

type BitmapLines []BitmapLine
type BitmapLine []byte

func (v *BitmapLines) Unmarshal(dec *Decoder, info *ValueInfo) {
	bmp, ok := dec.Arg().(*BitmapInfoHeader)
	if !ok || bmp == nil {
		panic("decode arg not set")
	}

	bytePerLine := int((int32(bmp.BitCount)*bmp.Width + 31) / 32 * 4)
	lineNum := int(bmp.Height)
	upend := true
	if lineNum < 0 {
		lineNum = -lineNum
		upend = false
	}

	*v = make([]BitmapLine, lineNum)
	for i := 0; i < lineNum; i++ {
		id := i
		if upend {
			id = lineNum - i - 1
		}
		newDec := dec.SubDecoder(bytePerLine)
		newDec.Decode(&(*v)[i], &ValueInfo{name: strconv.Itoa(id), info: "行"})
		dec.Seek(newDec.Pos())
	}
}

/****************** 图标&光标 **********************/

type IconFile struct {
	Header  IconDir
	Bitmaps []IconBitmap
}

func (v *IconFile) Unmarshal(dec *Decoder, info *ValueInfo) {
	dec.Decode(&v.Header, &ValueInfo{name: "Header"})
	v.Bitmaps = make([]IconBitmap, v.Header.Count)
	dec.Decode(&v.Bitmaps, &ValueInfo{name: "Bitmaps"})
}

type IconDir struct {
	Reserved      uint16
	Type          IconType
	Count         uint16
	IconEntries   []IconDirEntry
	CursorEntries []CursorDirEntry
}

func (v *IconDir) Unmarshal(dec *Decoder, info *ValueInfo) {
	dec.Decode(&v.Reserved, &ValueInfo{name: "Reserved"})
	dec.Decode(&v.Type, &ValueInfo{name: "Type", info: "文件类型"})
	dec.Decode(&v.Count, &ValueInfo{name: "Count", info: "图象个数"})
	if v.Type == 1 {
		v.IconEntries = make([]IconDirEntry, v.Count)
		dec.Decode(&v.IconEntries, &ValueInfo{name: "Entries"})
	} else {
		v.CursorEntries = make([]CursorDirEntry, v.Count)
		dec.Decode(&v.CursorEntries, &ValueInfo{name: "Entries"})
	}
}

type IconType uint16

func (v IconType) String() string {
	if v == 1 {
		return "icon"
	} else if v == 2 {
		return "cursor"
	} else {
		panic("file isn't icon or cursor format")
	}
}

type IconDirEntry struct {
	Width       uint8 `info:"宽度(像素)"`
	Height      uint8 `info:"高度(像素)"`
	ColorCount  uint8 `info:"颜色数"`
	Reserved    uint8
	Planes      uint16 `info:"平面数"`
	BitCount    uint16 `info:"比特/像素"`
	BytesInRes  uint32 `info:"位图数据大小"`
	ImageOffset uint32 `info:"位图数据偏移"`
}

type CursorDirEntry struct {
	Width       uint8 `info:"宽度(像素)"`
	Height      uint8 `info:"高度(像素)"`
	ColorCount  uint8 `info:"颜色数"`
	Reserved    uint8
	HotspotX    uint16 `info:"热点X"`
	HotspotY    uint16 `info:"热点Y"`
	BytesInRes  uint32 `info:"点阵数据大小"`
	ImageOffset uint32 `info:"点阵数据偏移"`
}

type IconBitmap struct {
	Info     BitmapInfoHeader
	Quad     BitmapQuad   `info:"调色板"`
	XorLines IconXorLines `info:"XOR点阵数据"`
	AndLines IconAndLines `info:"AND点阵数据"`
}

type IconXorLines []BitmapLine

func (v *IconXorLines) Unmarshal(dec *Decoder, info *ValueInfo) {
	bmp, ok := dec.Arg().(*BitmapInfoHeader)
	if !ok || bmp == nil {
		panic("decode arg not set")
	}

	bytePerLine := int((int32(bmp.BitCount)*bmp.Width + 31) / 32 * 4)
	lineNum := int(bmp.Height) / 2
	upend := true
	if lineNum < 0 {
		lineNum = -lineNum
		upend = false
	}

	*v = make([]BitmapLine, lineNum)
	for i := 0; i < lineNum; i++ {
		id := i
		if upend {
			id = lineNum - i - 1
		}
		newDec := dec.SubDecoder(bytePerLine)
		newDec.Decode(&(*v)[i], &ValueInfo{name: strconv.Itoa(id), info: "行"})
		dec.Seek(newDec.Pos())
	}
}

type IconAndLines []BitmapLine

func (v *IconAndLines) Unmarshal(dec *Decoder, info *ValueInfo) {
	bmp, ok := dec.Arg().(*BitmapInfoHeader)
	if !ok || bmp == nil {
		panic("decode arg not set")
	}

	bytePerLine := int((bmp.Width + 31) / 32 * 4)
	lineNum := int(bmp.Height) / 2
	upend := true
	if lineNum < 0 {
		lineNum = -lineNum
		upend = false
	}

	*v = make([]BitmapLine, lineNum)
	for i := 0; i < lineNum; i++ {
		id := i
		if upend {
			id = lineNum - i - 1
		}
		newDec := dec.SubDecoder(bytePerLine)
		newDec.Decode(&(*v)[i], &ValueInfo{name: strconv.Itoa(id), info: "行"})
		dec.Seek(newDec.Pos())
	}
}
